﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Aleator.Core
{
    /// <summary>
    /// Properties and methods determining chord behavior
    /// </summary>
    public class Chord
    {
        #region Members

        private ScaleDegree[] m_majorScaleDegrees = new ScaleDegree[7] { ScaleDegree.I, ScaleDegree.ii, ScaleDegree.iii, ScaleDegree.IV, ScaleDegree.V, ScaleDegree.vi, ScaleDegree.viiDim };
        private ScaleDegree[] m_minorScaleDegrees = new ScaleDegree[7] { ScaleDegree.i, ScaleDegree.iiDim, ScaleDegree.III, ScaleDegree.iv, ScaleDegree.v, ScaleDegree.VI, ScaleDegree.VII };
        private ScaleDegree[] m_harmonicMinorScaleDegrees = new ScaleDegree[7] { ScaleDegree.i, ScaleDegree.iiDim, ScaleDegree.IIIAug, ScaleDegree.iv, ScaleDegree.V, ScaleDegree.VI, ScaleDegree.viiDim };

        //Keep lists of notes at each interval for on-the-fly inversions
        private List<ConcreteNote> m_roots;
        private List<ConcreteNote> m_thirds;
        private List<ConcreteNote> m_fifths;
        private List<ConcreteNote> m_sevenths;

        private ScaleDegree m_degree;
        private ConcreteNote m_root;
        private Token m_token;

        private bool m_isInverted;

        #endregion

        #region Constructors

        /// <summary>
        /// Main contstructor
        /// </summary>
        /// <param name="root">Root note for this triad</param>
        /// <param name="interval">The scale degree the root note corresponds to</param>
        /// <param name="inverted">Is the chord eligible for inversion?</param>
        public Chord(ConcreteNote root, int interval, bool inverted, int notes = 0)
        {
            int third = interval + 1;
            int fifth = interval + 3;
            int seventh = interval + 5;

            m_root = root;
            m_isInverted = inverted;

            Notes = new List<ConcreteNote>();

            m_roots = new List<ConcreteNote>();
            m_thirds = new List<ConcreteNote>();
            m_fifths = new List<ConcreteNote>();
            m_sevenths = new List<ConcreteNote>();

            //TODO: Account for the top end of the octave
            m_roots.AddRange(CircleOfFifths.Notes.Where(n => n.Interval == interval - 1));

            if (interval > 5)
                m_thirds.AddRange(CircleOfFifths.Notes.Where(n => n.Interval == third - 7));
            else
                m_thirds.AddRange(CircleOfFifths.Notes.Where(n => n.Interval == third));

            if (interval > 3)
                m_fifths.AddRange(CircleOfFifths.Notes.Where(n => n.Interval == fifth - 7));
            else
                m_fifths.AddRange(CircleOfFifths.Notes.Where(n => n.Interval == fifth));

            Notes.AddRange(m_roots);
            Notes.AddRange(m_thirds);
            Notes.AddRange(m_fifths);

            //TODO: Experiment with creating various seventh combinations
            //Seventh chords
            if (notes == 4)
            {
                if (interval > 1)
                    m_sevenths.AddRange(CircleOfFifths.Notes.Where(n => n.Interval == seventh - 7));
                else
                    m_sevenths.AddRange(CircleOfFifths.Notes.Where(n => n.Interval == seventh));

                Notes.AddRange(m_sevenths);
            }

            //TODO: Figure out how to retrieve token for 7th chords
            switch (CircleOfFifths.CurrentModeName)
            {
                case CircleOfFifths.ModeName.Ionian:
                    NameIonianChord(interval);
                    break;
                case CircleOfFifths.ModeName.Aeolian:
                    NameAolianChord(interval);
                    break;
                case CircleOfFifths.ModeName.MelodicMinor:
                    NameMelodicMinorChord(interval);
                    break;
                case CircleOfFifths.ModeName.HarmonicMinor:
                    NameHarmonicMinorChord(interval);
                    break;
            }
        }

        #endregion

        #region Enums
        #pragma warning disable 1591

        /// <summary>
        /// Scale degree symbols
        /// </summary>
        public enum ScaleDegree
        {
            i,
            I,
            ii,
            iiDim,
            II,
            iii,
            III,
            IIIAug,
            iv,
            IV,
            v,
            V,
            vi,
            VI,
            vii,
            viiDim,
            VII
        }

        /// <summary>
        /// Chord tokens
        /// </summary>
        public enum Token
        {
            Major,
            Minor,
            Diminished,
            Augmented,
            Maj7,
            Dom7,
            Min7,
            MinMaj7,
            Sixth,
            SuspendedSecond,
            SuspendedFourth,
            SuspendedFourthSixth
        }

        #pragma warning restore 1591
        #endregion

        #region Properties

        /// <summary>
        /// Notes comprising this chord
        /// </summary>
        public List<ConcreteNote> Notes = new List<ConcreteNote>();

        /// <summary>
        /// Scale degree of this chord
        /// </summary>
        public ScaleDegree Degree
        {
            get
            {
                return m_degree;
            }
        }

        /// <summary>
        /// Chord name as displayed on the UI
        /// </summary>
        public string DisplayName
        {
            get
            {
                return string.Format("{0}{1} ({2})", Root.Name, m_token, Degree);
            }
        }

        /// <summary>
        /// Root note of this chord
        /// </summary>
        public ConcreteNote Root
        {
            get
            {
                return m_root;
            }
        }

        /// <summary>
        /// Boolean value indicating whether or not the chord has been inverted
        /// </summary>
        public Boolean IsInverted
        {
            get
            {
                return m_isInverted;
            }
        }

        /// <summary>
        /// Token to be added to the note value when name is displayed
        /// </summary>
        public Token ChordToken
        {
            get
            {
                return m_token;
            }
        }

        /// <summary>
        /// Available root notes to be included across all octaves being employed
        /// </summary>
        public List<ConcreteNote> Roots
        {
            get
            {
                return m_roots;
            }
        }

        /// <summary>
        /// Available thirds across all playing octaves
        /// </summary>
        public List<ConcreteNote> Thirds
        {
            get
            {
                return m_thirds;
            }
        }

        /// <summary>
        /// Available fifths across all playing octaves
        /// </summary>
        public List<ConcreteNote> Fifths
        {
            get
            {
                return m_fifths;
            }
        }

        #endregion

        #region Methods

        private void NameIonianChord(int interval)
        {
            if (interval == 1 || interval == 4 || interval == 5)
                m_token = Token.Major;
            else if (interval == 7)
                m_token = Token.Diminished;
            else
                m_token = Token.Minor;

            m_degree = m_majorScaleDegrees[interval - 1];
        }

        private void NameAolianChord(int interval)
        {
            if (interval == 1 || interval == 4 || interval == 5)
                m_token = Token.Minor;
            else if (interval == 2)
                m_token = Token.Diminished;
            else
                m_token = Token.Major;

            m_degree = m_minorScaleDegrees[interval - 1];
        }

        private void NameMelodicMinorChord(int interval)
        {
            if (interval == 3)
                m_token = Token.Augmented;
            else if (interval == 4 || interval == 5)
                m_token = Token.Major;
            else if (interval == 6 || interval == 7)
                m_token = Token.Diminished;
            else
                m_token = Token.Minor;

            m_degree = m_minorScaleDegrees[interval];
        }

        private void NameHarmonicMinorChord(int interval)
        {
            if (interval == 1 || interval == 4)
                m_token = Token.Minor;
            else if (interval == 2 || interval == 7)
                m_token = Token.Diminished;
            else if (interval == 3)
                m_token = Token.Augmented;
            else
                m_token = Token.Major;

            m_degree = m_minorScaleDegrees[interval];
        }

        /// <summary>
        /// Returns the interval of a given note
        /// </summary>
        /// <param name="n">The note whose interval is being requested</param>
        /// <returns></returns>
        public string GetChordInterval(ConcreteNote n)
        {
            if (m_roots.Contains(n))
                return "Root";
            else if (m_thirds.Contains(n))
                return "3rd";
            else if (m_fifths.Contains(n))
                return "5th";
            else
                return null;
        }

        #endregion

    }
}
